﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using System.Security.Cryptography;
using System.Web.Mvc;
using WinFormVideoServer.Models;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using Microsoft.AspNet.SignalR;
using WinFormVideoServer.Hubs;
using WinFormVideoServer.Common;

namespace WinFormVideoServer.Controllers
{
    public class VideoController : Controller
    {

        [HttpGet]
        public ActionResult Index()
        {
            //GlobalHost.ConnectionManager.GetHubContext<VideoHub>().Clients.All.ReloadRemoteVideoList();
            return View();
        }
        /// <summary>
        /// 通知客户端更新
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public ActionResult NoticeClientToUpdate()
        {
            GlobalHost.ConnectionManager.GetHubContext<VideoHub>().Clients.All.OnServerFileUpdated();
            return Content("");
        }

        private static string GetMd5(Stream stream)
        {
            byte[] hashData =  MD5.Create().ComputeHash(stream);
            StringBuilder sb = new StringBuilder();
            foreach (var item in hashData)
            {
                sb.Append(item.ToString("x2"));
            }
            return sb.ToString();
        }
        public ActionResult DeleteFiles(string [] fileNames)
        {
            string FileSavedDirecoty = Path.Combine(Server.MapPath("~"), "Upload");//文件上传物理目录
            ResponseModel model = new ResponseModel("删除成功", 0);
            if(fileNames == null || fileNames.Length <=0)
            {
                model.Code = -1;
                model.Message = "删除的文件不能为空";
                return Json(model);
            }
            int errorCount = 0;
            List<string> deletedFileList = new List<string>();
            foreach (var item in fileNames)
            {
                try
                {
                    string fileFullName = Path.Combine(FileSavedDirecoty, item);
                    if(!System.IO.File.Exists(fileFullName))
                    {
                        continue;
                    }
                    System.IO.File.Delete(fileFullName);
                    deletedFileList.Add(item);
                }
                catch
                {
                    model.Code = -2;
                    errorCount ++;
                }
            }
            if(model.Code !=0)
            {
                model.Message = $"{errorCount}个文件删除失败";
            }
            GlobalHost.ConnectionManager.GetHubContext<VideoHub>().Clients.All.OnServerFileDeleted(deletedFileList);
            return Json(model);
        }

        //private ReaderWriterLockSlim fileLock = new ReaderWriterLockSlim();
        /// <summary>
        /// 文件上传接口
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<ActionResult> Upload()
        {
            ResponseModel model = new ResponseModel("上传成功", 1);

            if (Request.Files.Count <= 0)
            {
                model.Code = -1;
                model.Message = "上传文件为空";
                return Json(model);
            }
            int chunkIndex = 0;
            int chunksCount = 0;
            long reqSize = 0;
            string reqMd5 = "";
            string shortFileName = "";
            var upPartFile = Request.Files[0];
            if(upPartFile.ContentLength <=0)
            {
                return Json(model);
            }

            //chunk和chunks参数只有需要分片的文件才有
            if (!string.IsNullOrEmpty(Request.Form["chunk"]) && !int.TryParse(Request.Form["chunk"], out chunkIndex))
            {
                model.Code = -2;
                model.Message = "chunk参数有误";
            }
            else if (!string.IsNullOrEmpty(Request.Form["chunks"]) && !int.TryParse(Request.Form["chunks"], out chunksCount))
            {
                model.Code = -3;
                model.Message = "chunks参数有误";
            }

            else if (string.IsNullOrEmpty(shortFileName = Request.Form["name"]))
            {
                model.Code = -4;
                model.Message = "文件名为空";
            }
            else if (string.IsNullOrEmpty(reqMd5 = Request.Form["md5"]))
            {
                model.Code = -5;
                model.Message = "md5值不能为空";
            }
            else if (string.IsNullOrEmpty(Request.Form["size"]) || !long.TryParse(Request["size"],out reqSize))
            {
                model.Code = -6;
                model.Message = "size值不能为空";
            }
            else
            {
                string fileExtension = Path.GetExtension(shortFileName);//文件扩展名
                string sysFileSavedDirecotry = Path.Combine(Server.MapPath("~"), "Upload");//文件上传物理目录
                string savedFileName = $"{Request.Form["md5"]}{fileExtension}";//保存文件名
                string savedFullFileName = Path.Combine(sysFileSavedDirecotry, savedFileName);//保存文件全名

                //fileLock.EnterReadLock();
                var existsFileInfo = new FileInfo(savedFullFileName);
                if(existsFileInfo!=null && existsFileInfo.Exists && existsFileInfo.Length == reqSize)
                {
                    model.Code = 2;
                    model.Message = "文件秒传成功";
                    //fileLock.ExitReadLock();
                    return Json(model);
                }
                //fileLock.ExitReadLock();
                //fileLock.EnterWriteLock();
                
                //文件大小和当前文件分片大小一样，视为未分片文件，直接保存，不启用分片写入
                if(reqSize == upPartFile.InputStream.Length)
                {
                    model.Code = 0;
                    model.Message = "所有分片上传完成";
                    using (FileStream fs = new FileStream(savedFullFileName,FileMode.CreateNew))
                    {
                        upPartFile.InputStream.CopyTo(fs);
                    }
                }
                else
                {
                    #region 文件分片写入逻辑
                    string tempDirectory = Path.Combine(sysFileSavedDirecotry, $"{reqMd5}.{fileExtension}");
                    if (!Directory.Exists(tempDirectory))
                    {
                        Directory.CreateDirectory(tempDirectory);
                    }
                    string savedTempFileName = Path.Combine(tempDirectory, $"{chunkIndex}.tmp");
                    if (System.IO.File.Exists(savedTempFileName))
                    {
                        System.IO.File.Delete(savedTempFileName);
                    }
                    using (FileStream fs = new FileStream(savedTempFileName, FileMode.CreateNew))
                    {
                        await upPartFile.InputStream.CopyToAsync(fs);
                    }
                    if (chunkIndex + 1 == chunksCount)//上传完成后删除临时文件
                    {
                        model.Code = 0;
                        model.Message = "所有分片上传完成";

                        Task tk = JoinTempFileName(tempDirectory, savedFullFileName);
                        tk.GetAwaiter();
                        await tk.ContinueWith((o) =>
                        {
                            //删除临时文件夹
                            Directory.Delete(tempDirectory, true);
                        });
                        
                        
                        
                    }

                    #endregion
                }
                //using (FileStream fs = new FileStream(savedFullFileName, FileMode.Append))
                //{
                //    using (Stream s = upPartFile.InputStream)
                //    {
                //        s.CopyTo(fs);
                //    }
                //    if (fs.Length == reqSize)
                //    {
                //        model.Code = 0;//所有分片上传完成
                //        model.Message = "所有分片上传完成";
                //    }
                //}
                //fileLock.ExitWriteLock();

            }
            return Json(model);
        }


        /// <summary>
        /// 合并临时文件
        /// </summary>
        /// <param name="tempFileDirecotry">临时文件所在的目录</param>
        /// <param name="savedFileName">合并后保存的文件名（包含路径）</param>
        /// <param name="jonedCompletedCallback">文件合并完成后的回调</param>
        /// <returns></returns>
        public async Task JoinTempFileName(string tempFileDirecotry,string savedFileName,Action<string> jonedCompletedCallback=null)
        {
            if(string.IsNullOrEmpty(tempFileDirecotry) 
                || string.IsNullOrEmpty(savedFileName)
                || !Directory.Exists(tempFileDirecotry) 
                || Directory.GetFiles(tempFileDirecotry).Length <=0
                ) return;

            string[] tempFiles = Directory.GetFiles(tempFileDirecotry);
            //这一步至关重要，对分片文件进行排序
            
            tempFiles = tempFiles.OrderBy(k=>k,new NumFileNameCompare()).ToArray();
            using (FileStream fsJoined = new FileStream(savedFileName, FileMode.Append))
            {
                foreach (var item in tempFiles)
                {
                    using (FileStream fs = new FileStream(item, FileMode.Open, FileAccess.Read))
                    {
                        await fs.CopyToAsync(fsJoined);
                    }
                }
            }
            
        }

        public ActionResult GetFileList()
        {
            string sysFileSavedDirecotry = Path.Combine(Server.MapPath("~"), "Upload");//文件上传物理目录
            var directoryInfo = new DirectoryInfo(sysFileSavedDirecotry);
            var fileList =  directoryInfo.GetFiles().Select(f=>new {
                FileSize = f.Length,
                FileName = f.Name,
                FileExtension =f.Extension,
                FileHash = Path.GetFileNameWithoutExtension(f.Name),
                f.CreationTime
            });
            string jsonString =Newtonsoft.Json.JsonConvert.SerializeObject(fileList, new Newtonsoft.Json.JsonSerializerSettings()
            {
                DateFormatString = "yyyy-MM-dd HH:mm:ss"
            });
            return Content(jsonString, "application/json", Encoding.UTF8);
        }
        //ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();

        /// <summary>
        /// 分片下载文件
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="startIndex">开始索引</param>
        /// <param name="endIndex">结束索引</param>
        /// <param name="contentType"></param>
        /// <returns></returns>
        public ActionResult DownloadPartion(string fileName,int startIndex,int size)
        {
            ResponseModel model = new ResponseModel("读取成功", 0);
            string sysFileSavedDirecotry = Path.Combine(Server.MapPath("~"), "Upload");//文件上传物理目录
            string downFileFullName = Path.Combine(sysFileSavedDirecotry, fileName);

            if(!System.IO.File.Exists(downFileFullName))
            {
                model.Message = "文件不存在";
                model.Code = -1;
                return Json(model);
            }
            try
            {
                
                byte[] partitionBuffer = new byte[size];
                //rwLock.EnterReadLock();
                using (FileStream s = new FileStream(downFileFullName, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    if(startIndex+size>s.Length)
                    {
                        //rwLock.ExitReadLock();
                        s.Close();
                        model.Code = -2;
                        model.Message = "读取位置超出文件大小";
                        return Json(model);
                    }
                    else
                    {
                        s.Seek(startIndex, SeekOrigin.Begin);
                        s.Read(partitionBuffer, 0, size);
                    }
                }
                //rwLock.ExitReadLock();
                return File(partitionBuffer, ".*");
            }
            catch (Exception exc)
            {

                model.Code = -2;
                model.Message = exc.Message;
            }
            finally
            {
                //rwLock.ExitReadLock();
            }
            return Json(model);
        }


        public ActionResult Download(string fileName)
        {
            if(string.IsNullOrEmpty(fileName))
            {
                return HttpNotFound("未能找到该文件");
            }
            string sysFileSavedDirecotry = Path.Combine(Server.MapPath("~"), "Upload");//文件上传物理目录
            string downFileFullName = Path.Combine(sysFileSavedDirecotry, fileName);
            return File(downFileFullName,".*");
        }
        /// <summary>
        /// 所有文件上传成功
        /// </summary>
        /// <returns></returns>
        public ActionResult TotalFileUploadCompleted(string[] fileNames)
        {
            //通知客户端
            GlobalHost.ConnectionManager.GetHubContext<VideoHub>().Clients.All.OnServerFileUpdated();
            return Content("");
        }
    }
}